#include "stdogre.h"
#include "SAFrameListener.h"
#include "main.h"
#include <OgreTextAreaOverlayElement.h>

Event event_table[] = 
{
	{"BuildTrack",	SAFrameListener::BuildTrack},
	{"BuildTrain",	SAFrameListener::BuildTrain},
	{"TrainView",	SAFrameListener::TrainView},
	{"FreeView",	SAFrameListener::FreeView},
	{"Linear",	SAFrameListener::Linear},
	{"BSprine",	SAFrameListener::BSprine},
	{"HSprine",	SAFrameListener::HSprine},
	{"Bezier",	SAFrameListener::Bezier},
	{"TopView",	SAFrameListener::TopView},
	{"Line",	SAFrameListener::Line},
	{"Road",	SAFrameListener::Road},
	
	{"", NULL}
};

SAFrameListener::SAFrameListener(Ogre::SceneManager* _SceneMgr, Ogre::RenderWindow* win, Ogre::Camera* cam, HikariManager_Sptr hikariMgr, bool bufferedKeys /*= false*/, bool bufferedMouse /*= false*/, bool bufferedJoy /*= false */ ) :
mCamera(cam), mTranslateVector(Ogre::Vector3::ZERO), mCurrentSpeed(0), mWindow(win), mStatsOn(true), mNumScreenShots(0),
mMoveScale(0.0f), mRotScale(0.0f), mTimeUntilNextToggle(0), mFiltering(Ogre::TFO_BILINEAR),
mAniso(1), mSceneDetailIndex(0), mMoveSpeed(100), mRotateSpeed(36),
mInputManager(0), mMouse(0), mKeyboard(0), mJoy(0),m_ShutDown(0),m_hikariMgr(hikariMgr),
m_SceneMgr(_SceneMgr)
{
	Ogre::LogManager::getSingletonPtr()->logMessage("*** Initializing OIS ***");
	OIS::ParamList pl;
	size_t windowHnd = 0;
	std::ostringstream windowHndStr;

	win->getCustomAttribute("WINDOW", &windowHnd);
	windowHndStr << windowHnd;
	pl.insert(std::make_pair(std::string("WINDOW"), windowHndStr.str()));

	mInputManager = OIS::InputManager::createInputSystem( pl );

	//Create all devices (We only catch joystick exceptions here, as, most people have Key/Mouse)
	mKeyboard = static_cast<OIS::Keyboard*>(mInputManager->createInputObject( OIS::OISKeyboard, bufferedKeys ));

	mMouse = static_cast<OIS::Mouse*>(mInputManager->createInputObject( OIS::OISMouse, bufferedMouse ));
	try {
		mJoy = static_cast<OIS::JoyStick*>(mInputManager->createInputObject( OIS::OISJoyStick, bufferedJoy ));
	}
	catch(...) {
		mJoy = 0;
	}

	//Set initial mouse clipping size
	windowResized(mWindow);

	//Register as a Window listener
	Ogre::WindowEventUtilities::addWindowEventListener(mWindow, this);
	//show stats
	m_SdkTrayListener = SharePtrNew;	
	m_TrayMgr = shared_ptr<OgreBites::SdkTrayManager>(new OgreBites::SdkTrayManager(
		"InterfaceName", win, mMouse, m_SdkTrayListener.get()));
	m_TrayMgr->showFrameStats(OgreBites::TL_BOTTOMLEFT);
	//mTrayMgr->showLogo(OgreBites::TL_BOTTOMRIGHT);
	m_mousekey[0]=0;
	m_mousekey[1]=0;
	m_mousekey[2]=0;
	// create a params panel for displaying sample details
	Ogre::StringVector items;
	items.push_back("cam.pX");
	items.push_back("cam.pY");
	items.push_back("cam.pZ");
	items.push_back("");
	items.push_back("cam.oW");
	items.push_back("cam.oX");
	items.push_back("cam.oY");
	items.push_back("cam.oZ");
	items.push_back("");
	items.push_back("Filtering");
	items.push_back("Poly Mode");
	m_DetailsPanel = m_TrayMgr->createParamsPanel(OgreBites::TL_NONE, "DetailsPanel", 200, items);
	m_DetailsPanel->setParamValue(9, "Bilinear");
	m_DetailsPanel->setParamValue(10, "Solid");
	m_DetailsPanel->hide();
	m_RayScnQuery = m_SceneMgr->createRayQuery(Ogre::Ray());
	m_GUIInteractive = new GUIInteractive(m_SceneMgr, this);
	// register function
	m_FlashControl = m_hikariMgr->getFlashControl("Controls");
	
	for (int i=0;event_table[i].id;i++)
	{
		m_eventmap[event_table[i].name] = event_table[i].id;
		m_FlashControl->bind(event_table[i].name.c_str(),
			Hikari::FlashDelegate(this, &SAFrameListener::FlCall_GetGuiEvent));
	}
}

bool SAFrameListener::frameRenderingQueued( const Ogre::FrameEvent& evt )
{
	if(mWindow->isClosed())	return false;
	mSpeedLimit = mMoveScale * evt.timeSinceLastFrame;
	//Need to capture/update each device
	mKeyboard->capture();
	mMouse->capture();
 	m_hikariMgr->injectMouseMove(mMouse->getMouseState().X.abs, mMouse->getMouseState().Y.abs);
 	m_hikariMgr->injectMouseWheel(mMouse->getMouseState().Z.rel);
	
	for (int i=0;i<3;i++)
	{
		if (mMouse->getMouseState().buttonDown((OIS::MouseButtonID)i) && !m_mousekey[i])
		{
			m_mousekey[i] = 1;
			m_hikariMgr->injectMouseDown(i);
		}
		if (!(mMouse->getMouseState().buttonDown((OIS::MouseButtonID)i)) && m_mousekey[i])
		{
			m_mousekey[i] = 0;
			m_hikariMgr->injectMouseUp(i);
		}
	}
	if( mJoy ) mJoy->capture();
	bool buffJ = (mJoy) ? mJoy->buffered() : true;
	Ogre::Vector3 lastMotion = mTranslateVector;
	//Check if one of the devices is not buffered
	if( !mMouse->buffered() || !mKeyboard->buffered() || !buffJ )
	{
		// one of the input modes is immediate, so setup what is needed for immediate movement
		if (mTimeUntilNextToggle >= 0)
			mTimeUntilNextToggle -= evt.timeSinceLastFrame;
		// Move about 100 units per second
		mMoveScale = mMoveSpeed * evt.timeSinceLastFrame;
		// Take about 10 seconds for full rotation
		mRotScale = mRotateSpeed * evt.timeSinceLastFrame;
		mRotX = 0;
		mRotY = 0;
		mTranslateVector = Ogre::Vector3::ZERO;
	}
	//Check to see which device is not buffered, and handle it
	if( !mKeyboard->buffered() )
		if( processUnbufferedKeyInput(evt) == false )
			return false;
	if( !mMouse->buffered() )
		if( processUnbufferedMouseInput(evt) == false )
			return false;
	// ramp up / ramp down speed
	if (mTranslateVector == Ogre::Vector3::ZERO)
	{
		// decay (one third speed)
		mCurrentSpeed -= evt.timeSinceLastFrame * 0.3;
		mTranslateVector = lastMotion;
	}
	else
	{
		// ramp up
		mCurrentSpeed += evt.timeSinceLastFrame;
	}
	// Limit motion speed
	if (mCurrentSpeed > 1.0)
		mCurrentSpeed = 1.0;
	if (mCurrentSpeed < 0.0)
		mCurrentSpeed = 0.0;
	mTranslateVector *= mCurrentSpeed;
	if( !mMouse->buffered() || !mKeyboard->buffered() || !buffJ )
		moveCamera();
	// show stats
	m_TrayMgr->refreshCursor();
	m_TrayMgr->frameRenderingQueued(evt);
	UpdateRayTracing();
	m_GUIInteractive->Update();
	return true;
}

bool SAFrameListener::frameEnded( const Ogre::FrameEvent& evt )
{
	return true;
}

void SAFrameListener::moveCamera()
{
	// Make all the changes to the camera
	// Note that YAW direction is around a fixed axis (freelook style) rather than a natural YAW
	//(e.g. airplane)
	mCamera->yaw(mRotX);
	mCamera->pitch(mRotY);
	mCamera->moveRelative(mTranslateVector);
}

bool SAFrameListener::processUnbufferedMouseInput( const Ogre::FrameEvent& evt )
{
	// Rotation factors, may not be used if the second mouse button is pressed
	// 2nd mouse button - slide, otherwise rotate
	const OIS::MouseState &ms = mMouse->getMouseState();
 	if( ms.buttonDown( OIS::MB_Right ) )
 	{
 		mTranslateVector.x += ms.X.rel * 0.5;
 		mTranslateVector.y -= ms.Y.rel * 0.5;
 	}
 	else if( ms.buttonDown( OIS::MB_Left ) )
 	{
 		mRotX = Ogre::Degree(-ms.X.rel * 0.05);
 		mRotY = Ogre::Degree(-ms.Y.rel * 0.05);
 	}
	mTranslateVector.z += -ms.Z.rel;

	return true;
}

bool SAFrameListener::processUnbufferedKeyInput( const Ogre::FrameEvent& evt )
{
	if(mKeyboard->isKeyDown(OIS::KC_UP))
		mTranslateVector.z = -mMoveScale*10;	// Move camera forward

	if(mKeyboard->isKeyDown(OIS::KC_DOWN))
		mTranslateVector.z = mMoveScale*10;	// Move camera backward

	if(mKeyboard->isKeyDown(OIS::KC_PGUP))
		mTranslateVector.y = mMoveScale;	// Move camera up

	if(mKeyboard->isKeyDown(OIS::KC_PGDOWN))
		mTranslateVector.y = -mMoveScale;	// Move camera down

	if(mKeyboard->isKeyDown(OIS::KC_Q))
		m_GUIInteractive->SetStats(GUIInteractive::MODE_LOOK);
	else if(mKeyboard->isKeyDown(OIS::KC_W))
		m_GUIInteractive->SetStats(GUIInteractive::MODE_MOVE);
	else if(mKeyboard->isKeyDown(OIS::KC_A))
		m_GUIInteractive->SetStats(GUIInteractive::MODE_ADD);
	else if(mKeyboard->isKeyDown(OIS::KC_R))
		m_GUIInteractive->SetStats(GUIInteractive::MODE_REMOVE);

	if(mKeyboard->isKeyDown(OIS::KC_LCONTROL))
		m_GUIInteractive->SetMoveRule(GUIInteractive::MODE_MOVE_Y);
	else
		m_GUIInteractive->SetMoveRule(GUIInteractive::MODE_MOVE_XZ);

	if(mKeyboard->isKeyDown(OIS::KC_K))
	{
		printf("mCamera = x:%f y:%f z:%f\n", mCamera->getPosition().x, mCamera->getPosition().y, mCamera->getPosition().z);
		Sleep(100);
	}

	if (mKeyboard->isKeyDown(OIS::KC_ESCAPE))
	{
		m_ShutDown = true;
	}
	return true;
}

SAFrameListener::~SAFrameListener()
{
	//Remove ourself as a Window listener
	Ogre::WindowEventUtilities::removeWindowEventListener(mWindow, this);
	windowClosed(mWindow);
}

void SAFrameListener::windowClosed( Ogre::RenderWindow* rw )
{
	//Only close for window that created OIS (the main window in these demos)
	if( rw == mWindow )
	{
		if( mInputManager )
		{
			mInputManager->destroyInputObject( mMouse );
			mInputManager->destroyInputObject( mKeyboard );
			mInputManager->destroyInputObject( mJoy );

			OIS::InputManager::destroyInputSystem(mInputManager);
			mInputManager = 0;
		}
	}
}

void SAFrameListener::windowResized( Ogre::RenderWindow* rw )
{
	unsigned int width, height, depth;
	int left, top;
	rw->getMetrics(width, height, depth, left, top);

	const OIS::MouseState &ms = mMouse->getMouseState();
	ms.width = width;
	ms.height = height;
}

Ogre::Vector2 SAFrameListener::getMousePos()
{
	return OgreBites::SdkTrayManager::sceneToScreen(mCamera, Ogre::Vector3::ZERO);
}

Ogre::Ray SAFrameListener::getMouseRay()
{
	return m_TrayMgr->getCursorRay(mCamera);
}

bool SAFrameListener::getMouseDown(int i)
{
	return m_mousekey[i];
}

void SAFrameListener::UpdateRayTracing()
{
	Ogre::Ray mouseRay = getMouseRay();
	Ogre::Vector2 mousePos = getMousePos();
	if (getMouseDown() && !m_hikariMgr->isAnyFocused())
	{
		m_RayScnQuery->setRay(mouseRay);
		m_RayScnQuery->setSortByDistance(true);
		m_RayScnQuery->setQueryMask(ControlPointManager::MASK);
		Ogre::RaySceneQueryResult& result = m_RayScnQuery->execute();
		Ogre::RaySceneQueryResult::iterator iter = result.begin();
		for(iter; iter != result.end(); iter++)
		{
			if(iter->movable && m_GUIInteractive->IsGuiObject(iter->movable))
			{
				m_GUIInteractive->UpdateClickObject(iter->movable);
				break;
			}
			else if (iter->worldFragment)
			{
				m_GUIInteractive->UpdateClickObject(NULL);
				m_GUIInteractive->AddPin(iter->worldFragment->singleIntersection);
			}
		}
	}
	else
	{
		m_GUIInteractive->UpdateMouseUp();
	}
}

Hikari::FlashValue SAFrameListener::FlCall_GetGuiEvent( Hikari::FlashControl* caller, const Hikari::Arguments& args )
{
	strmap::iterator it = m_eventmap.find(args[0].getString());
	if (it != m_eventmap.end())
	{
		switch (it->second)
		{
		case BuildTrack:
			m_GUIInteractive->BuildTrack();
			printf("%s\n", it->first.c_str());
			break;
		case BuildTrain:
			m_GUIInteractive->BuildTrain();
			printf("%s\n", it->first.c_str());
			break;
		case TrainView:
			m_GUIInteractive->SetTrainView(mCamera);
			printf("%s\n", it->first.c_str());
			break;
		case FreeView:
			m_GUIInteractive->SetFreeView(mCamera);
			printf("%s\n", it->first.c_str());
			break;
		case TopView:
			m_GUIInteractive->SetFreeView(mCamera);
			printf("%s\n", it->first.c_str());
			break;
		case Linear:
			m_GUIInteractive->SetTrackStyle(GUIInteractive::MODE_LINEAR);
			printf("%s\n", it->first.c_str());
			break;
		case BSprine:
			m_GUIInteractive->SetTrackStyle(GUIInteractive::MODE_BSPLINE);
			printf("%s\n", it->first.c_str());
			break;
		case HSprine:
			m_GUIInteractive->SetTrackStyle(GUIInteractive::MODE_HSPLINE);
			printf("%s\n", it->first.c_str());
			break;
		case Bezier:
			m_GUIInteractive->SetTrackStyle(GUIInteractive::MODE_BEZIER);
			printf("%s\n", it->first.c_str());
			break;
		case Line:
			m_GUIInteractive->SetRenderLine();
			printf("%s\n", it->first.c_str());
			break;
		case Road:
			m_GUIInteractive->SetRenderRoad();
			printf("%s\n", it->first.c_str());
			break;
		}
	}
	return FLASH_VOID;
}
